package org.geekbang.projects.rest.core;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.geektimes.base.util.ReflectionUtils;

import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.*;
import java.io.InputStream;
import java.net.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;

/**
* url连接支持类  
* @author : KangNing Hu
*/
public class URLHttpConnectionSupport extends HashMap<String , Object> {


	public final static String HEADER_ALLOW = "Allow";

	public final static String HEADER_COOKIE = "Set-Cookie";

	public final static String HEADER_LANGUAGE= "Content-Language";

	public final static String HEADER_LAST_MODIFIED = "Last-Modified";

	public final static String HEADER_LENGTH = "Content-Length";

	public final static String HEADER_LOCATION = "Content-Location";

	public final static String HEADER_TYPE = "Content-Type";

	private final static Map<String , String> DEFAULT_REQUEST_PROPERTY = new HashMap<>();

	protected AbstractExecutorService executor;

	static {
		DEFAULT_REQUEST_PROPERTY.put("Accept" , "*/*");
		DEFAULT_REQUEST_PROPERTY.put("Accept-Encoding" , "gzip, deflate, br");
		DEFAULT_REQUEST_PROPERTY.put("Accept-Language" , "zh-CN,zh;q=0.9");
		DEFAULT_REQUEST_PROPERTY.put("Cache-Control"  , "no-cache");
		DEFAULT_REQUEST_PROPERTY.put("Connection" , "keep-alive");
		DEFAULT_REQUEST_PROPERTY.put("User-Agent" , "my rest clint of java");
	}


	public URLHttpConnectionSupport(){
		//设置通用请求头
		this.putAll(DEFAULT_REQUEST_PROPERTY);
		//初始化默认线程池
		this.executor = new ThreadPoolExecutor(4 , 18 ,
				30 , TimeUnit.MINUTES  , new SynchronousQueue<Runnable>() ,
				new BasicThreadFactory.Builder().namingPattern("rest-client-").build());
	}
	/**
	 * 获取连接
	 * @param url 统一资源定位符
	 * @return
	 */
	protected URLConnection getConnection(URL url){
		try {
			HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
			forEach((name , value) -> urlConnection.addRequestProperty(name , value.toString()));
			return urlConnection;
		}catch (Exception e){
			throw new IllegalStateException(e);
		}
	}

	/**
	 * 创建响应
	 * @param defaultResponseClass
	 */
	protected Response createResponse(Class<? extends HttpResponse> defaultResponseClass , HttpURLConnection connection) {
		HttpResponse defaultResponse = ReflectionUtils.newInstance(defaultResponseClass);
		defaultResponse.setConnection(connection);
		defaultResponse.setHeaderString(getHeaders(connection));
		defaultResponse.setAllowedMethods(new HashSet<>(getHeaderList(HEADER_ALLOW , connection)));
		defaultResponse.setCookies(getCookie(connection));
		defaultResponse.setLanguage(new Locale(getHeader(HEADER_LANGUAGE , connection , "")));
		defaultResponse.setLastModified(parseStrToDate(getHeader(HEADER_LAST_MODIFIED , connection)));
		String length = getHeader(HEADER_LENGTH, connection);
		defaultResponse.setLength(StringUtils.isEmpty(length) ? -1 : Integer.valueOf(length) );
		defaultResponse.setLocation(getHeader(HEADER_LOCATION , connection));
		defaultResponse.setMediaType(getMediaType(connection));
		defaultResponse.setStatus(getStatus(connection));
		return defaultResponse;
	}


	/**
	 * 设置请求方法
	 * @param connection
	 * @param method
	 * @throws ProtocolException
	 */
	protected void setMethod(HttpURLConnection connection , String method){
		try {
			connection.setRequestMethod(method);
		}catch (Exception e){
			throw new IllegalStateException(e);
		}
	}

	/**
	 * 获取响应输出流
	 * @param connection 连接
	 * @return 返回响应信息的流对象
	 * @throws java.io.IOException
	 */
	private InputStream getInputStream(HttpURLConnection connection){
		try {
			return connection.getInputStream();
		}catch (Exception e){
			throw new IllegalStateException(e);
		}
	}
	/**
	 * 获取响应码
	 * @param connection 连接
	 * @throws  java.io.IOException
	 * @return 返回网络码
	 */
	private int getStatus(HttpURLConnection connection) {
		try {
			return connection.getResponseCode();
		}catch (Exception e){
			throw new IllegalStateException(e);
		}
	}

	/**
	 * 将时间格式字符串转换成 Date对象
	 * @param dateStr 时间格式字符串
	 * @throws ParseException
	 * @return
	 */
	private Date parseStrToDate(String dateStr) {
		try {
			if (StringUtils.isEmpty(dateStr)){
				return null;
			}
			return new SimpleDateFormat().parse(dateStr);
		}catch (Exception e){
			throw new IllegalArgumentException(e);
		}
	}


	/**
	 * 获取请求头
	 * @param connection
	 * @return
	 */
	private MultivaluedHashMap<String ,String> getHeaders(URLConnection connection){
		MultivaluedHashMap<String ,String> headers = new MultivaluedHashMap<>();
		Map<String, List<String>> headerFields = connection.getHeaderFields();
		if (!MapUtils.isEmpty(headerFields)){
			headerFields.forEach((key , value) -> headers.put(key , value));
		}
		return headers;
	}

	/**
	 * 获取cookie
	 * @param connection
	 * @return
	 */
	private Map<String ,NewCookie> getCookie(URLConnection connection){
		List<String> headerList = getHeaderList(HEADER_COOKIE, connection);
		if (CollectionUtils.isEmpty(headerList)){
			return new HashMap<>();
		}
		Map<String , NewCookie> cookieMap = new HashMap<>();
		for (String cookieAttr : headerList){
			NewCookie cookie = NewCookie.valueOf(cookieAttr);
			cookieMap.put(cookie.getName() , cookie);
		}
		return cookieMap;
	}

	/**
	 * 获取响应类型
	 * @param connection
	 * @return
	 */
	private MediaType getMediaType(URLConnection connection){
		String typeStr = getHeader(HEADER_TYPE, connection);
		return MediaType.valueOf(typeStr);
	}

	/**
	 * 获取请求头
	 * @param name 头名称
	 * @param connection 连接
	 * @return 返回多个值
	 */
	private List<String> getHeaderList(String name, URLConnection connection){
		List<String> headerList = getHeaders(connection).get(name);
		if (CollectionUtils.isEmpty(headerList)){
			return new ArrayList<>();
		}
		return headerList;
	}

	/**
	 * 获取请求头
	 * @param name 头名称
	 * @param connection 连接
	 * @return 返回第一个值
	 */
	private String getHeader(String name, URLConnection connection){
		return getHeaders(connection).getFirst(name);
	}

	/**
	 * 获取响应求头，如果没有则使用默认值
	 * @param name
	 * @param connection
	 * @param defaultValue
	 * @return
	 */
	private String getHeader(String name , URLConnection connection , String defaultValue){
		String header = getHeader(name, connection);
		return header == null ? defaultValue : header;
	}
}
